#include "thread.h"
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <semaphore.h>
#include "md5.h"
#include "generation_cle.h"


#define MAX_DL 5
#define MAX_PAIR 3
#define MAX_PIECE 10
#define FINI 4
#define VIDE 3
#define BACKLOG 10
#define MAX_CO 5

pthread_mutex_t tracker_mutex_rq;
pthread_mutex_t num_dl_mutex;
pthread_cond_t tracker_rq;
pthread_cond_t tracker_ans;
void * request[SIZE_CRITERION];
void * request_ans;
int request_nb;
int nb_dl;
int nb_co;
struct fichier_partage ** tab_fichier;
pthread_mutex_t * buffermap_mutex;
char ** tab_buffermap;
sem_t sem_co;

char dossier_donnees[100];
char dossier_config[100];
char dossier_dl[100];
int listen_port;
char tracker_ip[100];
int tracker_port;
int taille_piece; 
int max_connexion_pairs;
int max_pieces_par_pair;
int temps_maj_pair;
int max_dl_simul;


void lecture_fichier_config()
{//On recupere les informations du fichier de configuration.
  FILE *fichier_config = fopen("fichier_config.txt", "r");
  //Si le fichier n'existe pas on le creer avec les valeurs pas defaut.
  if(fichier_config == NULL)
    {
      fichier_config = fopen("fichier_config.txt","w");
      fprintf(fichier_config,"Dossier_Donnees:Donnees\nDossier_config:Metafiles\nDossier_dl:Téléchargements\nPort_ecoute:16000\nTracker_ip:127.0.0.1\nTracker_port:18000\nTaille_piece:2048\nMax_connexion_pair:5\nMax_pieces_par_dl:8\nTemps_entre_update:10\nMax_dl_simultane:10");
      strcpy(dossier_donnees,"Donnees");
      strcpy(dossier_config,"Metafiles");
      strcpy(dossier_dl,"Téléchargements");
      listen_port = 16000;
      strcpy(tracker_ip,"127.0.0.1");
      tracker_port = 18000;
      taille_piece = 2048; 
      max_connexion_pairs = 5;
      max_pieces_par_pair = 8;
      temps_maj_pair = 10;
      max_dl_simul = 5;
    }
  //Si il existe on parse les informations.
  else
    {
      int total=0;
      total+=fscanf(fichier_config, "Dossier_Donnees:%s\n",dossier_donnees);
      total+=fscanf(fichier_config, "Dossier_config:%s\n",dossier_config);
      total+=fscanf(fichier_config, "Dossier_dl:%s\n",dossier_dl);
      total+=fscanf(fichier_config, "Port_ecoute:%d\n",&listen_port);
      total+=fscanf(fichier_config, "Tracker_ip:%s\n",tracker_ip);
      total+=fscanf(fichier_config, "Tracker_port:%d\n",&tracker_port);    
      total+=fscanf(fichier_config, "Taille_piece:%d\n",&taille_piece);
      total+=fscanf(fichier_config, "Max_connexion_pair:%d\n",&max_connexion_pairs);
      total+=fscanf(fichier_config, "Max_pieces_par_dl:%d\n",&max_pieces_par_pair);
      total+=fscanf(fichier_config, "Temps_entre_update:%d\n",&temps_maj_pair);
      total+=fscanf(fichier_config, "Max_dl_simultane:%d",&max_dl_simul);
      //On verifi que le fichier est standard.
      if(total != 11)
	{
	  perror("ERROR fichier_config");
	  exit(-1);
	}
    }
  fclose(fichier_config);
  mkdir(dossier_dl,0755);
  mkdir(dossier_donnees,0755);
  mkdir(dossier_config,0755);
  printf("Lecture du fichier de configuration OK.\n");
  return;
}

int get_tab_piece(struct fichier_partage * fichier,char * buffermap,char * paire_buffermap,int num_dl,FILE * s_stream)
{//On selectionner les pieces que l'on veut télécharger et on va les télécharger.
  int * result = malloc(sizeof(int) * (MAX_PIECE + 1));
  int nb_piece = fichier->taille_fichier/fichier->taille_piece;
  if(fichier->taille_fichier%fichier->taille_piece!=0)
    nb_piece++;
  //On verouille le buffermap du fichier pour selectionner les pieces.
  pthread_mutex_lock(&buffermap_mutex[num_dl]);
  int j = 0;
  int i = 0;
  for(i = 0;i < nb_piece && j < MAX_PIECE;i++)
    { 
      //Si on trouve une piece que nous n'avons pas
      //on l'ajoute au tableau result et on change le tempbuffmap
      //pour tenir les autres thread au courant de l'avancement du téléchargement. 
      if((paire_buffermap[i/8] & ~buffermap[i/8]) & (1u << (i % 8)))
	{
	  buffermap[i/8] = buffermap[i/8] | (1u << (i % 8));
	  result[j]=i;
	  j++;
	}
    }
  //On laisse les autres threads prendre des pieces.
  pthread_mutex_unlock(&buffermap_mutex[num_dl]);
  //Si il n'y a pas de piece à télécharger on retourne VIDE.
  if(j == 0)
    return VIDE;
  result[j] = -1;
  send_getpieces(fichier->cle_fichier,result,s_stream);
  int err = 0;
  err = receive_data(fichier,s_stream,dossier_dl);
  //Si il y a eu une erreur dans la lecture du fichier
  //on remet le buffermap temporaire à sont état d'orrigine
  //pour pouvoir retélécharger les pieces.
  if(err != EXIT_SUCCESS)
    {
      pthread_mutex_lock(&buffermap_mutex[num_dl]);
      for(i = 0;result[i] != -1;i++)
	{
	  buffermap[i/8]=buffermap[i/8] & ~(1u << (i % 8));
	}
      pthread_mutex_unlock(&buffermap_mutex[num_dl]);
      return EXIT_FAILURE; 
    }
  //Si tout va bien on modifie le buffermap et on retourne.
  else
    {
      //On verouille donc l'acces au vrai buffermap et on le modifie.
      pthread_mutex_lock(&buffermap_mutex[num_dl]);
      for(i = 0;result[i] != -1;i++)
	{
	  fichier->buffermap[result[i]/8]=fichier->buffermap[result[i]/8] | (1u << (result[i] % 8));
	}
      modifie_buffermap(fichier,dossier_config,WRITE);
      //On verifie aussi qu'on est pas le dernier en quel cas on le présise par la valeur de retour.
      i = 0;
      while(i < nb_piece)
	{
	  if(!(fichier->buffermap[i/8] & (1u << (i % 8))))
	    {
	      break;
	    }
	  i++;
	}
      pthread_mutex_unlock(&buffermap_mutex[num_dl]);
      if(i == nb_piece)
	{
	  return FINI;
	}
    }
  return EXIT_SUCCESS;
}

struct fichier_partage* result_show(struct file * resultat)
{//On affiche la liste des fichier qui répondent à la recherche 
  //et on retourne le fichier choisi par l'utilisateur.
  struct fichier_partage* tempf=resultat->tete;
  int max_fichier=1;
  int i;
  char input [256];
  system("clear");
  printf("Quel fichier voulez vous télécharger ? (Entrez un numero) :\n");
  printf("---- ---- ----\n");
  //On boucle sur les fichiers résultant de la recherche. 
  while(tempf!=NULL){
    printf("(%i) : Nom : %s",max_fichier,tempf->nom_fichier);
    printf(" - Taille totale : %ld",tempf->taille_fichier);
    printf(" - Taille piece : %ld",tempf->taille_piece);
    printf(" - Cle : %s\n",tempf->cle_fichier);
    max_fichier++;
    tempf=tempf->next;
  }
  printf("(%i) : Quitter\n",max_fichier);
  printf("---- ---- ----\n");
  //On boucle tant que l'utilisateur n'a pas donné
  //de réponse valide.
  do{
    fgets (input,256,stdin);
    i = atoi (input);
    if(i>max_fichier || i<1){
      printf("Veuillez entrer un choix valide:\n");
    }
  }while(i>max_fichier || i<1);
  //On retourne Null si l'utilisateur annule la recherche.
  if(i==max_fichier)
    return NULL;
  //Sinon on retourne le ficher choisi.
  tempf=resultat->tete;
  for(int j=1;j<i;j++){
    tempf=tempf->next;
  }
  return tempf;
}

void * serveur_thread(void * data)
{//On traite ici les demande d'envoi de piece par un autre paire 
  //une fois la connection au paire effecuté.
  char *  cle = NULL;
  struct fichier_partage * fichier = NULL;
  int * piecelist = NULL;
  int s = *(int *)data;
  int * s_num= (int *) data; 
  FILE * s_stream = fdopen(s,"r+");
  if(s_stream == NULL)
    {
      perror("ERROR stream");
      return NULL;
    }
  //On génère la file des fichier partageable.
  struct file * file_fichier = liste_fichier(dossier_donnees,taille_piece);
  struct file * file_dl = lire_file_dl(dossier_config);
  struct file * file=malloc(sizeof(struct file));
  file_fichier->queue->next=file_dl->tete;
  file->tete=file_fichier->tete;
  file->queue=file_dl->queue;
  int requestnb = readstart(s_stream);
  char * dossier;
  struct fichier_partage * tempfichier;
  //Puis on attend les requettes du paire
  while(requestnb > 0)
    {
      cle=NULL;
      fichier=NULL;
      piecelist=NULL;
      tempfichier=NULL;
      dossier=dossier_dl;
      switch(requestnb)
	{
	case 1: //Si la requette est un interested.
	  {
	    cle = receive_interested(s_stream);
	    if(cle == NULL)
	      {
		*s_num=-1;
		sem_post(&sem_co);
		perror("ERROR interested");
		return NULL;
	      }
	    fichier = file->tete;
	    while((fichier != NULL) && (strcmp(cle,fichier->cle_fichier) != 0))
	      {
		fichier = fichier->next;
	      }
	    if(fichier == NULL)
	      {
		*s_num=-1;
		sem_post(&sem_co);
		perror("ERROR fichier do not exist");
		return NULL;
	      }
	    reply_have(fichier,s_stream);
	    break;
	  }
	case 2: //Si la requette est un getpieces.
	  {
	    piecelist = receive_getpieces(file,&fichier,s_stream);
	    if(piecelist == NULL)
	      {
		*s_num=-1;
		sem_post(&sem_co);
		perror("ERROR receive getpieces");
		return NULL;
	      }
	    tempfichier=file_fichier->tete;
    	    while(tempfichier != file_fichier->queue->next)
	      {
		if(strcmp(tempfichier->cle_fichier,fichier->cle_fichier) == 0)
		  {
		    dossier=dossier_donnees;
		  }
		tempfichier=tempfichier->next;
	      }
	    reply_data(fichier,piecelist,s_stream,dossier);
	    break;
	  }
	default ://On sort car on a fini et on a coupe la connection.
	  {
	    *s_num=-1;
	    sem_post(&sem_co);
	    return NULL;
	    break;
	  }
	}
      requestnb=readstart(s_stream);
    }
  *s_num=-1;
  sem_post(&sem_co);
  return NULL;
}

void * lancer_serveur(void * data)
{//On boucle pour attendre les connection des autres paires
  // et lancer un thread_serveur qui traitera leur demande
  int s;
  struct sockaddr_in sa;
  int * tab_s = malloc(sizeof(int)*max_connexion_pairs);
  for(int i = 0;i < max_connexion_pairs;i++)
    {
      tab_s[i] = -1; 
    }
  sem_init(&sem_co,0,max_connexion_pairs);
  struct sockaddr_in ** tab_sa = malloc(sizeof(struct sockaddr_in*)*max_connexion_pairs);
  pthread_t * tab_thread = malloc(sizeof(pthread_t)*max_connexion_pairs);
  nb_co = 0;
  int k;
  //On allout un descripteur de socket
  if((s = socket(AF_INET,SOCK_STREAM,0)) < 0)
    {
      perror("ERROR socket");
      return NULL;
    }
  memset(&sa,0,sizeof(struct sockaddr_in));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(listen_port);
  sa.sin_addr.s_addr = htonl(INADDR_ANY);
  //On fait le lien entre la socket et l'adresse IP + port.
  if(bind(s,(struct sockaddr *)&sa,sizeof(sa)) < 0)
    {
      perror("ERROR bind");
      return NULL;
    }
  if(listen(s,BACKLOG) == -1)
    {
      perror("ERROR listen");
      return NULL;
    }
  //Puis on boucle sur l'attente d'une connection à la socket
  while(1)
    {
      sem_wait(&sem_co);
      for(k = 0;k < max_connexion_pairs && tab_s[k] > 0;k++);
      tab_s[k] = accept(s,(struct sockaddr *)&tab_sa[k],&(socklen_t){sizeof tab_sa[k]});
      if(tab_s[k] < 0)
	{
	  perror("ERROR accept");
	  return NULL;
	}
      pthread_create(&tab_thread[nb_co],NULL,serveur_thread,(void*)&tab_s[k]);
    }
  return NULL;
}

void* dl_thread(void* data)
{//On recupere les information de connexion
  //puis on lance la recherche de piece manquante
  //et le téléchargement de piece
  //pour finalement vérifier que tout c'est bien passé.
  char * tempc=strdup((char*)(((void**)data)[0]));
  char * ip=strtok(tempc,":");
  int port=atoi(strtok(NULL,":"));
  int num_dl=*((int*)(((void**)data)[1]));
  char* pair_buffermap;
  int s;
  FILE * s_stream;
  struct sockaddr_in sa;
  //On se connecte au pair pour lui demander son buffermap.
  if((s = socket(AF_INET,SOCK_STREAM,0))<0){
    perror("ERROR socket");
    return NULL;
  }
  memset(&sa,0,sizeof(struct sockaddr_in));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  sa.sin_addr.s_addr = inet_addr(ip);
  if(connect (s,(const struct sockaddr *)&sa,sizeof(sa)) <0){
    perror("ERROR connect");
    return NULL;
  }
  s_stream=fdopen(s,"r+");
  int err = EXIT_SUCCESS;
  //On boucle tant que notre buffermap n'est pas plein.
  while(err == EXIT_SUCCESS)
    {
      //On demande au pair son buffermap.
      send_interested(tab_fichier[num_dl]->cle_fichier,s_stream);
      pair_buffermap=receive_have(tab_fichier[num_dl],s_stream);
      if(pair_buffermap == NULL)
	{
	  perror("ERROR invalide buffermap");
	  return NULL;
	}
      //Puis on lance le téléchargement de piece.
      err=get_tab_piece(tab_fichier[num_dl],tab_buffermap[num_dl],pair_buffermap,num_dl,s_stream);
    }
  if(err == EXIT_FAILURE)
    {
      perror("ERROR invalide pieces");
      return NULL;
    }
  //Si on a totalement rempli le buffermap on verifie la cle MD5
  //et on transfer le fichier dans le dossier Donnees
  if(err == FINI)
    {
      FILE * in;
      char*tampon=malloc(sizeof(char)*(strlen(tab_fichier[num_dl]->nom_fichier)+strlen(dossier_dl)+2));
      tampon=strcpy(tampon,dossier_dl);
      tampon=strcat(tampon,"/");
      tampon=strcat(tampon,tab_fichier[num_dl]->nom_fichier);
      in=fopen(tampon,"r+");
      if(in == NULL)
	{
	  printf("Le fichier n'existe pas\n");
	  return NULL;//le fichier n'existe pas
	}
      char cle[16*2+1];
      md5_byte_t digest[16];
      int di;
      int nb_piece = tab_fichier[num_dl]->taille_fichier/tab_fichier[num_dl]->taille_piece;
      if(tab_fichier[num_dl]->taille_fichier%tab_fichier[num_dl]->taille_piece!=0)
	nb_piece++;
      generate_key(in,tab_fichier[num_dl]->taille_fichier,nb_piece,tab_fichier[num_dl]->taille_piece,digest);
      fclose(in);
      for(di = 0; di < 16; ++di)
	snprintf(cle + di * 2, sizeof(cle)+di*2-1, "%02x", digest[di]);
      if(strcmp(cle,tab_fichier[num_dl]->cle_fichier) != 0)
	{
	  printf("Téléchargement de %s corrompu, veuillez relancer le téléchargement\n",tab_fichier[num_dl]->nom_fichier);
	}
      else
	{
	  //Si la cle est valide, on déplace le fichier.
	  char * tampon1=malloc(sizeof(char)*(strlen(dossier_donnees)+strlen(tab_fichier[num_dl]->nom_fichier)+2));
	  char * tampon2=malloc(sizeof(char)*(strlen(dossier_dl)+strlen(tab_fichier[num_dl]->nom_fichier)+2));
	  strcpy(tampon1,dossier_donnees);
	  strcat(tampon1,"/");
	  strcat(tampon1,tab_fichier[num_dl]->nom_fichier);
	  strcpy(tampon2,dossier_dl);
	  strcat(tampon2,"/");
	  strcat(tampon2,tab_fichier[num_dl]->nom_fichier);	 
	  rename(tampon2,tampon1);
	  printf("Téléchargement de %s fini\n",tab_fichier[num_dl]->nom_fichier);
	}
      //Finalement on supprime le metafichier.
      supprime_file_dl(tab_fichier[num_dl],dossier_config);
      pthread_mutex_lock(&num_dl_mutex);
      nb_dl--;
      pthread_mutex_unlock(&num_dl_mutex);
    }
  return NULL;
}
 
int lancer_dl(struct fichier_partage * fichier)
{//On lance les differents thread pour le téléchargement du fichier.
  struct timeval now;
  struct timespec timeout;
  char ** tab_ipport;
  int num_dl;
  //On lock la creation/suppression de téléchargement.
  pthread_mutex_lock(&num_dl_mutex);
  //On prend un nouveau num de téléchargement.
  num_dl=++nb_dl;
  //ICI voir gestion du tab_fichier (semaphores ...)
  tab_fichier[num_dl]=fichier;
  tab_buffermap[num_dl]=malloc(sizeof(char)*fichier->taille_buffermap);
  memcpy(tab_buffermap[num_dl],fichier->buffermap,fichier->taille_buffermap);
  //On delock la creation/suppression de téléchargement.
  pthread_mutex_unlock(&num_dl_mutex);
  //On lock l'envoi de requet au thread tracker.
  pthread_mutex_lock(&tracker_mutex_rq);
  //On prépare la requette.
  request_nb=2;
  request[0]=(void*)fichier;
  //On envoi la demande .
  pthread_cond_signal(&tracker_rq);
  //On prepare le timer de 5s.
  gettimeofday(&now,NULL);
  timeout.tv_sec = now.tv_sec + 5;
  timeout.tv_nsec = now.tv_usec * 1000; 
  int retcode=0;
  //On attend le timer ou la reponse du thread tracker.
  retcode=pthread_cond_timedwait(&tracker_ans,&tracker_mutex_rq,&timeout);
  //On regarde si le timer nous reveille.
  if(retcode==ETIMEDOUT){
    printf("Connection avec le tracker perdu, fermeture de l'application.\n");
    return EXIT_FAILURE;
  }
  //Sinon on recupere la réponse du tracker.
  tab_ipport=(char **)request_ans;
  //Et on delock l'envoi de signaux au tracker.
  pthread_mutex_unlock(&tracker_mutex_rq);
  if(tab_ipport == NULL)
    {
      printf("Téléchargement de %s interrompu par manque de sources.\n",fichier->nom_fichier);
    }
  else
    {
      //On creer le metafichier .dl si il n'existe pas.
      if(creer_file_dl(fichier,dossier_config) == EXIT_FAILURE)
	{//On modifie le buffermap si le metafichier .dl existe.
	  modifie_buffermap(fichier,dossier_config,READ);
	}
      pthread_mutex_init(&buffermap_mutex[num_dl],NULL);
      //On lance finalement le bon nombre de thread pour dl.
      pthread_t dl[max_dl_simul];
      void ** arg[2];
      int * argint=malloc(sizeof(int));
      *argint=num_dl;
      arg[1]=(void*)argint;     
      for(int k=0; (k < max_dl_simul) && (tab_ipport[k] != NULL);k++)
	{
	  arg[0]=(void*)tab_ipport[k];
	  pthread_create(&dl[k],NULL,dl_thread,(void *)arg);
	}
    }
  return EXIT_SUCCESS;
}

int reprise_dl()
{//On regarde les metafichier qui existent et on lance leur téléchargement
  struct file * fichier_dl = lire_file_dl(dossier_config);
  struct fichier_partage * fichier = fichier_dl->tete;
  while(fichier != NULL)
    {
      printf("Reprise du téléchargement de %s.\n",fichier->nom_fichier);
      lancer_dl(fichier);
      fichier=fichier->next;
    }
  return EXIT_SUCCESS;
}

void* tracker_thread(void* data)
{//On s'occupe ici de tout les communication avec le tracker
  //ainsi que de l'envoi des updates.
  int s;
  FILE* s_stream;
  struct timeval now;
  struct timespec timeout;
  struct sockaddr_in sa;
  if((s = socket(AF_INET,SOCK_STREAM,0))<0){
    perror("ERROR socket");
    return NULL;
  }
  memset(&sa,0,sizeof(struct sockaddr_in));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(tracker_port);
  sa.sin_addr.s_addr = inet_addr(tracker_ip);
  if(connect (s,(const struct sockaddr *)&sa,sizeof(sa)) <0){
    perror("ERROR connect");
    exit(-1);
  }
  s_stream=fdopen(s,"r+");
  send_announce(listen_port, dossier_donnees, dossier_config, taille_piece, s_stream);
  if(receive_announce(s_stream) != EXIT_SUCCESS){
    perror("ERROR receive annouce");
    exit(-1);
  }
  pthread_cond_signal(&tracker_ans);
  while(1){
    //attente de la requette
    gettimeofday(&now,NULL);
    timeout.tv_sec = now.tv_sec + temps_maj_pair;//On met un timer de 5s
    timeout.tv_nsec = now.tv_usec * 1000;
    if(pthread_cond_timedwait(&tracker_rq,&tracker_mutex_rq,&timeout)==ETIMEDOUT){
      send_update(dossier_donnees, dossier_config, taille_piece, s_stream);
      if(receive_announce(s_stream)!=EXIT_SUCCESS){
	perror("ERROR receive update");
	exit(-1);
      }
      pthread_mutex_unlock(&tracker_mutex_rq);
    }
    else{
      switch(request_nb){
      case(1)://On reçoit un look
	{
	  send_look((char **)request,s_stream);
	  request_ans=(void*)creer_file();
	  if(receive_look((struct file *)request_ans,s_stream,taille_piece) != EXIT_SUCCESS)
	    {
	      perror("ERROR receive look");
	      return NULL;
	    }
	  break;
	}
      case(2)://On reçoit un get_file
	{
	  send_getfile(((struct fichier_partage **)request)[0]->cle_fichier,s_stream);
	  request_ans=(void **)receive_getfile(((struct fichier_partage **)request)[0],s_stream);
	}
      }
      pthread_cond_signal(&tracker_ans);
      pthread_mutex_unlock(&tracker_mutex_rq);
    }
  }
  return NULL;
}

int main(void)
{
  lecture_fichier_config();
  struct timeval now;
  struct timespec timeout;
  pthread_mutex_init(&num_dl_mutex,NULL);
  nb_dl=-1;
  pthread_mutex_init(&tracker_mutex_rq,NULL);
  pthread_cond_init(&tracker_rq,NULL);
  pthread_cond_init(&tracker_ans,NULL);
  tab_fichier=malloc(sizeof(struct fichier_partage *)*max_dl_simul);
  buffermap_mutex=malloc(sizeof(num_dl_mutex)*max_dl_simul);
  tab_buffermap=malloc(sizeof(char *)*max_dl_simul);
  pthread_t tracker;
  system("clear");
  printf("Connection au tracker ...\n");
  pthread_create(&tracker,NULL,tracker_thread,NULL);
  gettimeofday(&now,NULL);
  timeout.tv_sec = now.tv_sec + 8;//On met un timer de 8s
  timeout.tv_nsec = now.tv_usec * 1000;
  if(pthread_cond_timedwait(&tracker_ans,&tracker_mutex_rq,&timeout)==ETIMEDOUT){
    printf("Impossible de se connecter avec le tracker.\n");
    printf("Fermeture de l'application ...\n");
    return EXIT_FAILURE;
  }
  pthread_mutex_unlock(&tracker_mutex_rq);
  pthread_t serveur;
  pthread_create(&serveur,NULL,lancer_serveur,NULL);
  printf("Connection effectuée\n");
  //On relance les téléchargements
  if(reprise_dl() != EXIT_SUCCESS){
    perror("ERROR reprise dl");
    return EXIT_FAILURE;
  }
  // fpurge(stdin); à viré par la suite et à placer plus loin
  int i;
  char input [256];
  char * critere [SIZE_CRITERION];
  struct fichier_partage * fichier_dl;
  system("clear");
  for(;;){
    critere[0]=NULL;
    printf("Quel type de fichier cherchez vous ? (Entrez un nombre) :\n");
    printf("---- ---- ----\n");
    printf("(1) : Faire une recherche par nom\n");
    printf("(2) : Faire une recherche par taille\n");
    printf("(3) : Faire une recherche par nom et par taille\n");
    printf("(4) : Quitter\n");
    printf("---- ---- ----\n");
    fgets (input,256,stdin);
    i = atoi (input);
    switch(i){
    case(4):
      {
	printf("Fermeture de l'application ...\n");
	return EXIT_SUCCESS;
	break;
      }
    case(1):
      {//Recherche par nom
	system("clear");
	printf("Quel est le nom complet du fichier que vous voulez télécharger ?:\n");
	printf("---- ---- ----\n");
	printf("(1) : Annuler cette recherche\n");
	printf("(2) : Quitter\n");
	printf("---- ---- ----\n");
	fgets (input,256,stdin);
	i = atoi (input);
	switch(i){
	case(1):
	  break;
	case(2):
	  {
	    printf("Fermeture de l'application ...\n");
	    return EXIT_SUCCESS;
	    break;
	  }
	default:
	  {
	    sscanf(input,"%s",input);
	    critere[0]=malloc(sizeof(char)*(strlen(input)+strlen("filename=\"\"")));
	    sprintf(critere[0],"filename=\"%s\"",input);
	    critere[1]=NULL;
	    printf("critere :%s\n",critere[0]);
	    //Reste à lancer le DL
	    break;
	  }
	}    
	break;
	//Fin de la recherche par nom
      }
    case(2):
      {//Recherche par taille
	system("clear");
	printf("Quel est la taille minimal du fichier que vous voulez télécharger ?:\n");
	printf("---- ---- ----\n");
	printf("(1) : Annuler cette recherche\n");
	printf("(2) : Quitter\n");
	printf("---- ---- ----\n");
	fgets (input,256,stdin);
	i = atoi (input);
	switch(i){
	case(1):
	  break;
	case(2):
	  {
	    printf("Fermeture de l'application ...\n");
	    return EXIT_SUCCESS;
	    break;
	  }
	default:
	  {
	    sscanf(input,"%s",input);
	    i = -1;
	    i = atoi (input);
	    if(i >= 0)
	      {
		critere[0]=malloc(sizeof(char)*(strlen(input)+strlen("filename=\"\"")));
		sprintf(critere[0],"filesize>\"%i\"",i);
		critere[1]=NULL;
		//Reste à lancer le DL
	      }
	    else
	      {
		printf("Taille invalide\n");
	      }
	    break;
	  }
	}    
	break;
	//Fin de la recherche par taille
      }
    case(3):
      {//Recherche par taille et par nom
	system("clear");
	printf("Quel est la taille minimal du fichier que vous voulez télécharger ?:\n");
	printf("---- ---- ----\n");
	printf("(1) : Annuler cette recherche\n");
	printf("(2) : Quitter\n");
	printf("---- ---- ----\n");
	fgets (input,256,stdin);
	i=0;
	i = atoi (input);
	switch(i){
	case(1):
	  break;
	case(2):
	  {
	    printf("Fermeture de l'application ...\n");
	    return EXIT_SUCCESS;
	    break;
	  }
	default:
	  {
	    sscanf(input,"%s",input);
	    i = -1;
	    i = atoi (input);
	    if(i >= 0)
	      {
		critere[0]=malloc(sizeof(char)*(strlen(input)+strlen("filename=\"\"")));
		sprintf(critere[0],"filesize>\"%i\"",i);
		critere[1]=NULL;
		//Reste à lancer le DL
	      }
	    else
	      {
		printf("Taille invalide\n");
	      }
	    break;
	  }
	}
	system("clear");
	printf("Quel est le nom complet du fichier que vous voulez télécharger ?:\n");
	printf("---- ---- ----\n");
	printf("(1) : Annuler cette recherche\n");
	printf("(2) : Quitter\n");
	printf("---- ---- ----\n");
	fgets (input,256,stdin);
	i=0;
	i = atoi (input);
	switch(i){
	case(1):
	  break;
	case(2):
	  {
	    printf("Fermeture de l'application ...\n");
	    return EXIT_SUCCESS;
	    break;
	  }
	default:
	  {
	    sscanf(input,"%s",input);
	    critere[1]=malloc(sizeof(char)*(strlen(input)+strlen("filename=\"\"")));
	    sprintf(critere[1],"filename=\"%s\"",input);
	    critere[2]=NULL;
	    printf("critere :%s\n",critere[0]);
	    //Reste à lancer le DL
	    break;
	  }
	}    
	break;
      }//Fin de la recherche par taille et par nom
    default:
      {
	printf("Veuillez entrer un choix valide:\n");
	break;
      }
    }
    //On a choisi notre critere
    if(critere[0] != NULL)
      {
	//On verrouille l'envoi de signaux au thread tracker
	pthread_mutex_lock(&tracker_mutex_rq);
	//On prépare la requette
	int k=0;
	for(k=0;critere[k]!=NULL;k++)
	  {
	    request[k]=(void*)critere[k];
	  }
	request[k]=NULL;
	request_nb=1;
	//On envoi le signal
	pthread_cond_signal(&tracker_rq);
	//On prépare le timer de 8 s
	gettimeofday(&now,NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	int retcode=0;
	//On attend que le tacker ai fini et nous reveille ou que le timer passe
	retcode=pthread_cond_timedwait(&tracker_ans,&tracker_mutex_rq,&timeout);
	//Si c'est le timer qui nous reveille
	if(retcode==ETIMEDOUT){
	  printf("Connection avec le tracker perdu, fermeture de l'applicatioue.\n");
	  return EXIT_FAILURE;
	}
	//On regarde si il y a des resultats
	if(((struct file *)request_ans)->tete != NULL)
	  {
	    //On recupere alors la réponse du tracker
	    fichier_dl=result_show((struct file *)request_ans);
	    //On délock l'envoi de signaux au thread tacker
	    pthread_mutex_unlock(&tracker_mutex_rq);
	    //Si l'utilisateur ne choisi pas de fichier on passe
	    if(fichier_dl != NULL)
	      {
		lancer_dl(fichier_dl);
	      }
	    system("clear");
	  }
	else
	  { 
	    pthread_mutex_unlock(&tracker_mutex_rq);
	    system("clear");
	    printf("Aucun fichier ne correspond à votre recherche\n");
	    printf("---- ---- ----\n");
	  }
      }
  }
  return EXIT_SUCCESS;
}
